"""
FFmpeg元数据提取器

使用FFmpeg/FFprobe提取详细的视频和音频元数据。
"""

import asyncio
import json
import os
from typing import Dict, Any, Union, Optional
from datetime import datetime
from pathlib import Path
import logging

from src.core.di import Injectable, Inject
from .base import MetadataExtractor, VideoMetadata, AudioMetadata


@Injectable(scope="singleton")
class FFmpegMetadataExtractor(MetadataExtractor):
    """
    FFmpeg元数据提取器
    
    使用FFprobe提取详细的媒体文件元数据。
    """
    
    def __init__(self,
                 config: Dict[str, Any] = Inject("config"),
                 logger: logging.Logger = Inject("logger")):
        super().__init__()
        self.config = config
        self.logger = logger
        
        # FFmpeg配置
        self.ffprobe_path = self._get_ffprobe_path()
        
        # 支持的格式
        self.video_formats = [
            "mp4", "avi", "mov", "mkv", "webm", "flv", "wmv", "m4v",
            "3gp", "asf", "rm", "rmvb", "vob", "ts", "mts", "m2ts"
        ]
        
        self.audio_formats = [
            "mp3", "wav", "aac", "flac", "ogg", "wma", "m4a", "ac3",
            "dts", "ape", "opus", "amr", "aiff"
        ]
    
    def _get_ffprobe_path(self) -> str:
        """获取FFprobe可执行文件路径"""
        ffprobe_path = self.config.get("ffmpeg", {}).get("ffprobe_path", "ffprobe")
        
        try:
            # 测试FFprobe是否可用
            import subprocess
            subprocess.run([ffprobe_path, "-version"],
                         capture_output=True, check=True, timeout=5)
            return ffprobe_path
        except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
            import shutil
            path = shutil.which("ffprobe")
            if path:
                return path
            
            raise RuntimeError("未找到FFprobe可执行文件，请安装FFmpeg")
    
    def get_supported_formats(self) -> list[str]:
        """获取支持的文件格式"""
        return self.video_formats + self.audio_formats
    
    async def extract_metadata(self, file_path: str, **kwargs) -> Union[VideoMetadata, AudioMetadata]:
        """提取文件元数据"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")
        
        file_type = self.get_file_type(file_path)
        
        if file_type == "video":
            return await self.extract_video_metadata(file_path, **kwargs)
        elif file_type == "audio":
            return await self.extract_audio_metadata(file_path, **kwargs)
        else:
            # 尝试作为视频文件处理
            try:
                return await self.extract_video_metadata(file_path, **kwargs)
            except Exception:
                # 如果失败，尝试作为音频文件处理
                return await self.extract_audio_metadata(file_path, **kwargs)
    
    async def extract_video_metadata(self, video_path: str, **kwargs) -> VideoMetadata:
        """提取视频元数据"""
        self.logger.info(f"提取视频元数据: {video_path}")
        
        # 获取FFprobe数据
        probe_data = await self._run_ffprobe(video_path)
        
        # 解析元数据
        metadata = self._parse_video_metadata(video_path, probe_data)
        
        self.logger.info(f"视频元数据提取完成: {metadata.filename}")
        return metadata
    
    async def extract_audio_metadata(self, audio_path: str, **kwargs) -> AudioMetadata:
        """提取音频元数据"""
        self.logger.info(f"提取音频元数据: {audio_path}")
        
        # 获取FFprobe数据
        probe_data = await self._run_ffprobe(audio_path)
        
        # 解析元数据
        metadata = self._parse_audio_metadata(audio_path, probe_data)
        
        self.logger.info(f"音频元数据提取完成: {metadata.filename}")
        return metadata
    
    async def _run_ffprobe(self, file_path: str) -> Dict[str, Any]:
        """运行FFprobe获取元数据"""
        cmd = [
            self.ffprobe_path,
            "-v", "quiet",
            "-print_format", "json",
            "-show_format",
            "-show_streams",
            "-show_chapters",
            file_path
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "未知错误"
            raise RuntimeError(f"FFprobe执行失败: {error_msg}")
        
        try:
            return json.loads(stdout.decode())
        except json.JSONDecodeError as e:
            raise RuntimeError(f"解析FFprobe输出失败: {e}")
    
    def _parse_video_metadata(self, file_path: str, probe_data: Dict[str, Any]) -> VideoMetadata:
        """解析视频元数据"""
        format_info = probe_data.get("format", {})
        streams = probe_data.get("streams", [])
        chapters = probe_data.get("chapters", [])
        
        # 基本文件信息
        file_stat = os.stat(file_path)
        path_obj = Path(file_path)
        
        metadata = VideoMetadata(
            filename=path_obj.name,
            filepath=str(path_obj.absolute()),
            file_size=int(format_info.get("size", file_stat.st_size)),
            duration=float(format_info.get("duration", 0)),
            format_name=format_info.get("format_name", ""),
            format_long_name=format_info.get("format_long_name", ""),
            container_bitrate=int(format_info.get("bit_rate", 0)),
            modification_time=datetime.fromtimestamp(file_stat.st_mtime)
        )
        
        # 解析流信息
        video_stream = None
        audio_stream = None
        
        for stream in streams:
            if stream.get("codec_type") == "video" and not video_stream:
                video_stream = stream
            elif stream.get("codec_type") == "audio" and not audio_stream:
                audio_stream = stream
        
        # 视频流信息
        if video_stream:
            metadata.width = int(video_stream.get("width", 0))
            metadata.height = int(video_stream.get("height", 0))
            metadata.video_codec = video_stream.get("codec_name", "")
            metadata.video_bitrate = int(video_stream.get("bit_rate", 0))
            metadata.pixel_format = video_stream.get("pix_fmt", "")
            metadata.frame_count = int(video_stream.get("nb_frames", 0))
            
            # 计算帧率
            fps_str = video_stream.get("r_frame_rate", "0/1")
            metadata.fps = self._parse_fps(fps_str)
            
            # 宽高比
            dar = video_stream.get("display_aspect_ratio", "")
            if dar:
                metadata.aspect_ratio = dar
            elif metadata.width and metadata.height:
                from math import gcd
                g = gcd(metadata.width, metadata.height)
                metadata.aspect_ratio = f"{metadata.width//g}:{metadata.height//g}"
            
            # 颜色信息
            metadata.color_space = video_stream.get("color_space", "")
            metadata.color_range = video_stream.get("color_range", "")
            metadata.color_primaries = video_stream.get("color_primaries", "")
            metadata.transfer_characteristics = video_stream.get("color_trc", "")
        
        # 音频流信息
        if audio_stream:
            metadata.audio_codec = audio_stream.get("codec_name", "")
            metadata.audio_bitrate = int(audio_stream.get("bit_rate", 0))
            metadata.sample_rate = int(audio_stream.get("sample_rate", 0))
            metadata.channels = int(audio_stream.get("channels", 0))
            metadata.audio_duration = float(audio_stream.get("duration", metadata.duration))
        
        # 元数据标签
        tags = format_info.get("tags", {})
        metadata.title = tags.get("title", "")
        metadata.artist = tags.get("artist", "")
        metadata.album = tags.get("album", "")
        metadata.genre = tags.get("genre", "")
        metadata.comment = tags.get("comment", "")
        metadata.copyright = tags.get("copyright", "")
        
        # 创建时间
        creation_time = tags.get("creation_time")
        if creation_time:
            try:
                metadata.creation_time = datetime.fromisoformat(creation_time.replace('Z', '+00:00'))
            except ValueError:
                pass
        
        # 章节信息
        metadata.chapters = []
        for chapter in chapters:
            chapter_info = {
                "id": chapter.get("id", 0),
                "start": float(chapter.get("start_time", 0)),
                "end": float(chapter.get("end_time", 0)),
                "title": chapter.get("tags", {}).get("title", "")
            }
            metadata.chapters.append(chapter_info)
        
        # 流信息
        metadata.streams = streams
        
        # 自定义元数据
        metadata.custom_metadata = {
            "probe_data": probe_data,
            "extractor": "ffmpeg",
            "extraction_time": datetime.now().isoformat()
        }
        
        return metadata
    
    def _parse_audio_metadata(self, file_path: str, probe_data: Dict[str, Any]) -> AudioMetadata:
        """解析音频元数据"""
        format_info = probe_data.get("format", {})
        streams = probe_data.get("streams", [])
        
        # 基本文件信息
        file_stat = os.stat(file_path)
        path_obj = Path(file_path)
        
        metadata = AudioMetadata(
            filename=path_obj.name,
            filepath=str(path_obj.absolute()),
            file_size=int(format_info.get("size", file_stat.st_size)),
            duration=float(format_info.get("duration", 0)),
            format_name=format_info.get("format_name", ""),
            format_long_name=format_info.get("format_long_name", "")
        )
        
        # 查找音频流
        audio_stream = None
        for stream in streams:
            if stream.get("codec_type") == "audio":
                audio_stream = stream
                break
        
        # 音频流信息
        if audio_stream:
            metadata.codec = audio_stream.get("codec_name", "")
            metadata.bitrate = int(audio_stream.get("bit_rate", 0))
            metadata.sample_rate = int(audio_stream.get("sample_rate", 0))
            metadata.channels = int(audio_stream.get("channels", 0))
            metadata.bit_depth = int(audio_stream.get("bits_per_sample", 0))
        
        # 元数据标签
        tags = format_info.get("tags", {})
        metadata.title = tags.get("title", "")
        metadata.artist = tags.get("artist", "")
        metadata.album = tags.get("album", "")
        metadata.genre = tags.get("genre", "")
        metadata.year = tags.get("date", tags.get("year", ""))
        metadata.track = tags.get("track", "")
        
        # 自定义元数据
        metadata.custom_metadata = {
            "probe_data": probe_data,
            "extractor": "ffmpeg",
            "extraction_time": datetime.now().isoformat()
        }
        
        return metadata
    
    def _parse_fps(self, fps_str: str) -> float:
        """解析帧率字符串"""
        try:
            if "/" in fps_str:
                numerator, denominator = fps_str.split("/")
                return float(numerator) / float(denominator)
            else:
                return float(fps_str)
        except (ValueError, ZeroDivisionError):
            return 0.0
    
    # 实现插件接口方法
    async def classify(self, input_data: Any, **kwargs) -> Any:
        """分类数据（不适用于元数据提取器）"""
        raise NotImplementedError("元数据提取器不支持分类功能")
    
    async def output(self, data: Any, **kwargs) -> Any:
        """输出数据（不适用于元数据提取器）"""
        raise NotImplementedError("元数据提取器不支持输出功能")
